package com.test

import com.annotations.DefaultValue
import com.entity.PackPath
import com.entity.yaml.AppConfig
import com.entity.yaml.Languages
import com.entity.yaml.XmlToTs
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper
import com.utils.YamlUtil
import io.github.stream29.jsonschemagenerator.SchemaBuildingContext
import io.github.stream29.jsonschemagenerator.SchemaGeneratingInstruction
import io.github.stream29.jsonschemagenerator.SchemaGenerator
import io.github.stream29.jsonschemagenerator.schemaOf
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.*
import kotlinx.serialization.serializer
import java.io.File
import kotlin.test.Test

class YamlTest {

    fun load() {
        YamlUtil.loadYaml("./config/config.yml")
        YamlUtil.loadYaml("./config/lang.yml")
    }

    @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
    @Test
    fun createYamlSchema() {
        val types = arrayOf(
            "array",
            "boolean",
            "integer",
            "null",
            "number",
            "object",
            "string"
        )

        val defaultSchemaGenerator = SchemaGenerator()
        fun SchemaBuildingContext.plusDefault(
            instruction: SchemaGeneratingInstruction,
            annotations: List<Annotation>
        ): JsonObject {
            val jsonObject = instruction()
            val defaultValue = annotations.filterIsInstance<DefaultValue>()
            println("${annotations.size}, ${annotations.joinToString()}")
            return if (defaultValue.isNotEmpty()) {
                buildJsonObject {
                    val defValue = defaultValue[0].default
                    println("primitive:" + defaultValue[0])
                    var valueStr = ""
                    jsonObject.entries.forEach { (key, value) ->
                        if (key == "description" && !(value as JsonPrimitive).content.also { valueStr = it }
                                .contains("默认")) {
                            put(key, JsonPrimitive("$valueStr, 默认: $defValue"))
                        } else put(key, value)
                    }
                    val content = when (val type = jsonObject["type"]) {
                        is JsonObject -> type.jsonPrimitive.content
                        is JsonArray -> type.getOrNull(0)?.jsonPrimitive?.content
                        is JsonPrimitive -> type.content
                        else -> null
                    }
                    if (defValue == "null") {
                        put("default", null)
                    } else when (content) {
                        "boolean" -> put("default", defValue.toBoolean())
                        "integer" -> put("default", defValue.toInt())
                        else -> put("default", defValue)
                    }
                }

            } else jsonObject
        }

        val config = SchemaGenerator(
            encodePrimitive = { plusDefault(defaultSchemaGenerator.encodePrimitive, annotations) },
            encodeEnum = { plusDefault(defaultSchemaGenerator.encodeEnum, annotations) },
            encodeClass = { plusDefault(defaultSchemaGenerator.encodeClass, annotations) },
            encodeArray = defaultSchemaGenerator.encodeArray,
            encodeMap = defaultSchemaGenerator.encodeMap,
            encodePolymorphic = defaultSchemaGenerator.encodePolymorphic,
            encodeContextual = {
                buildJsonObject {
                    annotations
                    descriptor
                    generator
                    putJsonArray("anyOf") {
                        types.forEach {
                            addJsonObject {
                                put("type", it)
                            }
                        }
                    }
                }
            },
            encodeObject = defaultSchemaGenerator.encodeObject
        )

        val serializationClass = listOf(AppConfig::class, Languages::class, XmlToTs::class)

        val properties = mutableMapOf<String, JsonObject>()
        serializationClass.forEach {
            val descriptor = it.serializer().descriptor
            val schema = config.schemaOf(descriptor, emptyList())
            properties[descriptor.serialName] = schema
        }

        val root = mapOf(
            "\$schema" to JsonPrimitive("http://json-schema.org/draft-07/schema"),
            "\$id" to JsonPrimitive("https://gitee.com/bogegit/PackProject/raw/master/src/main/resources/pack-schema.json"),
            "type" to JsonPrimitive("object"),
            "properties" to JsonObject(properties)
        )
        val jsonObject = JsonObject(root)

        val json = Json {
            prettyPrint = true
            prettyPrintIndent = "  "
        }
        val str = json.encodeToString(jsonObject)
//        println(str)

        PackPath("./src/main/resources/pack-schema.json").writeText(str)

    }

    @Test
    fun yamlMapperTest() {
        val mapper = YAMLMapper.builder()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false)
            .configure(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS, false)
            .configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, false)
            .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
            .build()
        val value = mapper.readValue(File("config/config2.yml"), AppConfig::class.java)

        println(value)

    }

    @Test
    fun testYaml() {

        load()


        val config = YamlUtil.getConfig("config", AppConfig::class)
        println(config)
        config?.compress?.js?.config?.plus("aaa")

        val config2 = YamlUtil.getConfig("config", AppConfig::class)
        println(
            config2
        )


    }

    @Test
    fun testConfigYaml() {
        load()

        val config = YamlUtil.getConfig("config", AppConfig::class)

        println(
            config
        )


    }

    @Test
    fun testLangYaml() {

        val config = YamlUtil.getConfig("xmlToTs", XmlToTs::class)
        println(
            config
        )

    }

}